using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;
using Etier.IconHelper;

namespace FSync
{
    /// <summary>
    /// The program form.
    /// </summary>
    public partial class MainForm : Form
    {
        #region Private Fields
        /// <summary>
        /// Watches any changes of file system.
        /// </summary>
        private FileSystemWatcher _watcher;
        /// <summary>
        /// icon manager.
        /// </summary>
        private IconListManager _iconListManager;
        /// <summary>
        /// Root tree node.
        /// </summary>
        TreeNode _rootNode;
        #endregion

        #region Public Functions
        /// <summary>
        /// MainForm ctor.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            _trvGroupedLogs.ImageList = _smallImageList;
            _iconListManager = new IconListManager(_smallImageList, IconReader.IconSize.Small);
            _watcher = new FileSystemWatcher();
            _rootNode = new TreeNode();
        }
        #endregion

        #region Private Functions
        /// <summary>
        /// Start watch any change of source folder.
        /// </summary>
        private void StartWatch()
        {
            if (string.IsNullOrEmpty(_tbxSourceFolder.Text) ||
                !Directory.Exists(_tbxSourceFolder.Text))
            {
                _rtbSequencialLogs.Text += "Source Folder doesn't initialize correctly!";
                return;
            }

            _watcher.Path = _tbxSourceFolder.Text;
            _watcher.IncludeSubdirectories = true;
            _watcher.EnableRaisingEvents = true;
            _watcher.NotifyFilter =
                NotifyFilters.DirectoryName
                | NotifyFilters.FileName
                | NotifyFilters.LastWrite
                | NotifyFilters.Attributes;
            
            _watcher.Renamed += new RenamedEventHandler(LogChanges);
            _watcher.Changed += new FileSystemEventHandler(LogChanges);
            _watcher.Created += new FileSystemEventHandler(LogChanges);
            _watcher.Deleted += new FileSystemEventHandler(LogChanges);
        }

        /// <summary>
        /// Stop watch.
        /// </summary>
        private void StopWatch()
        {
            _watcher.Renamed -= new RenamedEventHandler(LogChanges);
            _watcher.Changed -= new FileSystemEventHandler(LogChanges);
            _watcher.Created -= new FileSystemEventHandler(LogChanges);
            _watcher.Deleted -= new FileSystemEventHandler(LogChanges);

            _watcher = null;
        }

        /// <summary>
        /// Log changes.
        /// </summary>
        /// <param name="e"></param>
        private void LogChanges(object sender, FileSystemEventArgs e)
        {
            if (InvokeRequired)
            {
                WatcherDelegate dgt = new WatcherDelegate(LogChanges);
                BeginInvoke(dgt, new object[] { sender, e });
            }
            else
            {
                AddSequencialLog(e);
                AddGroupedLog(e);
            }
        }

        /// <summary>
        /// Log changes to SequencialChanges tab.
        /// </summary>
        /// <param name="e"></param>
        private void AddSequencialLog(EventArgs e)
        {
            if (!(e is FileSystemEventArgs))
                return;

            StringBuilder msg = new StringBuilder("[" + DateTime.Now.ToString() + "]-");

            FileSystemEventArgs arg = e as FileSystemEventArgs;
            msg.Append("[" + arg.ChangeType.ToString() + "]-");
            if (arg is RenamedEventArgs)
            {
                msg.Append("\"" + (arg as RenamedEventArgs).OldFullPath + "\" renamed to \"");
            }
            msg.Append("\"" + arg.FullPath + "\"");
            msg.Append("\n");

            _rtbSequencialLogs.Text += msg.ToString();
        }

        /// <summary>
        /// Log changes to GroupedChanges tab.
        /// </summary>
        /// <param name="e"></param>
        private void AddGroupedLog(EventArgs e)
        {
            if (!(e is FileSystemEventArgs))
                return;

            FileSystemEventArgs arg = e as FileSystemEventArgs;

            TreeNode currentNode = GetOrCreateCurrentNode(arg.FullPath);
            
            TreeNode node = new TreeNode();
            node.Text = arg.ChangeType.ToString();
            node.Name = arg.ChangeType.ToString();
            
            currentNode.Nodes.Add(node);

            SetTreeImages(_rootNode);
        }

        /// <summary>
        /// Get node being watched if exists, otherwise create it.
        /// </summary>
        /// <param name="fullPath"></param>
        /// <returns></returns>
        private TreeNode GetOrCreateCurrentNode(string fullPath)
        {
            if (string.IsNullOrEmpty(fullPath))
                return null;

            if (!fullPath.StartsWith(_rootNode.Text))
                return null;
            
            int index = _rootNode.Text.Length + 1;
            string subPath = fullPath.Substring(index);
            string[] folders = subPath.Split(Path.DirectorySeparatorChar);

            TreeNode currentNode = _rootNode;
            for (int i = 0; i < folders.Length; ++i)
            {
                if (currentNode.Nodes.ContainsKey(folders[i]))
                {
                    currentNode = currentNode.Nodes[folders[i]];
                }
                else
                {
                    TreeNode newNode = new TreeNode();
                    newNode.Name = folders[i];
                    newNode.Text = folders[i];
                    currentNode.Nodes.Add(newNode);

                    currentNode = newNode;
                }
            }
            return currentNode;
        }

        /// <summary>
        /// Set image recursive.
        /// </summary>
        /// <param name="root"></param>
        private void SetTreeImages(TreeNode root)
        {
            SetNodeImage(root);

            foreach (TreeNode node in root.Nodes)
            {
                SetTreeImages(node);
            }
        }

        /// <summary>
        ///  Set image for a TreeNode.
        /// </summary>
        /// <param name="node"></param>
        private void SetNodeImage(TreeNode node)
        {
            string fullPath = node.FullPath;
            if (Directory.Exists(fullPath))
            {
                node.ImageIndex = _iconListManager.TryGetFolderIcon(
                   IconReader.IconSize.Small, IconReader.FolderType.Closed);
                node.SelectedImageIndex = _iconListManager.TryGetFolderIcon(
                       IconReader.IconSize.Small, IconReader.FolderType.Open);
            }
            else if (File.Exists(fullPath))
            {
                node.ImageIndex = _iconListManager.TryGetFileIcon(fullPath);
                node.SelectedImageIndex = _iconListManager.TryGetFileIcon(fullPath);
            }
            else
            {
                // created, deleted, changed, renamed etc.
                if (node.Text == WatcherChangeTypes.Changed.ToString())
                {
                    node.ImageKey = WatcherChangeTypes.Changed.ToString();
                    node.SelectedImageKey = WatcherChangeTypes.Changed.ToString();
                }
                else if (node.Text == WatcherChangeTypes.Created.ToString())
                {
                    node.ImageKey = WatcherChangeTypes.Created.ToString();
                    node.SelectedImageKey = WatcherChangeTypes.Created.ToString();
                }
                else if (node.Text == WatcherChangeTypes.Deleted.ToString())
                {
                    node.ImageKey = WatcherChangeTypes.Deleted.ToString();
                    node.SelectedImageKey = WatcherChangeTypes.Deleted.ToString();
                }
                else if (node.Text == WatcherChangeTypes.Renamed.ToString())
                {
                    node.ImageKey = WatcherChangeTypes.Renamed.ToString();
                    node.SelectedImageKey = WatcherChangeTypes.Renamed.ToString();
                }
            }
        }

        private void ShowForm()
        {
            if (this.Visible)
                this.TopMost = true;
            else
                this.Visible = true;

            this.ShowInTaskbar = true;
            this.WindowState = FormWindowState.Normal;
            this.TopMost = false;
        }
        #endregion

        #region Delegates
        /// <summary>
        /// Delegate for Created, Changed, Deleted, and Renamed events.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        delegate void WatcherDelegate(object sender, FileSystemEventArgs e);
        #endregion

        #region Event Handlers
        /// <summary>
        /// Form load event handler.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_Load(object sender, EventArgs e)
        {
            System.Configuration.AppSettingsReader reader = new System.Configuration.AppSettingsReader();

            object srcFolder = reader.GetValue("srcFolder", typeof(String));
            if (srcFolder != null)
            {
                string src = srcFolder.ToString();
                if (!String.IsNullOrEmpty(src))
                {
                    this._tbxSourceFolder.Text = src;

                    _rootNode.Text = src;
                    _rootNode.Name = src;
                    _trvGroupedLogs.Nodes.Add(_rootNode);

                    SetTreeImages(_rootNode);
                }
            }

            object desFolder = reader.GetValue("desFolder", typeof(String));
            if (desFolder != null)
            {
                string des = desFolder.ToString();
                if (!String.IsNullOrEmpty(des))
                {
                    this._tbxDestinationFolder.Text = des;
                }
            }

            object filters = reader.GetValue("filters", typeof(string));
            if (filters != null)
            {
                string fil = filters.ToString();
                if (!string.IsNullOrEmpty(fil))
                {
                    _watcher.Filter = fil;
                }
            }

            StartWatch();
        }
        /// <summary>
        /// Form close event handler.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
        {

        }
        /// <summary>
        /// Close main form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _btnClose_Click(object sender, EventArgs e)
        {
            StopWatch();
            Close();
        }

        /// <summary>
        /// Apply changes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _btnApply_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(_tbxDestinationFolder.Text))
                return;
        }

        private void MainForm_SizeChanged(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.ShowInTaskbar = false;
                this.Visible = false;
            }
        }

        private void _btnSave_Click(object sender, EventArgs e)
        {

        }
        #endregion

        #region MenuItem Click Handlers
        private void _notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ShowForm();
        }
        private void showToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowForm();
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _btnClose_Click(sender, e);
        }
        #endregion
    }
}